package handler

import (
	"context"
	"manage/api/user"
	"manage/internal/biz"
	"manage/internal/routes/middleware"
	"manage/internal/routes/model"
	"manage/internal/service"
	"strings"

	"github.com/Fromsko/gouitls/auth"
	rp "github.com/Fromsko/gouitls/reply"
	"github.com/gin-gonic/gin"
)

// @Summary 用户注册
// @Description 用户注册接口
// @Tags Auth
// @Accept x-www-form-urlencoded
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Param email formData string true "邮箱"
// @Param role formData string false "角色，默认为学生"
// @Router /register [post]
func Register(srv *service.GatewayService) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		username := ctx.PostForm("username")
		password := ctx.PostForm("password")
		email := ctx.PostForm("email")
		role := ctx.DefaultPostForm("role", "学生")

		if username == "" || password == "" || email == "" {
			ctx.JSON(400, gin.H{"msg": "注册失败", "err": "参数不正确"})
			ctx.Abort()
			return
		}

		srv.Remote.CallGrpc(func(call *biz.APIUseCase) {
			call.Repo.User(func(c context.Context, usc user.UserServiceClient) {
				reply, _ := usc.AddUser(ctx, &user.AddUserRequest{
					Username: username,
					Password: password,
					Email:    email,
					Role:     role,
				})
				if reply.Code != 200 {
					ctx.JSON(int(reply.Code), gin.H{
						"err": "用户已存在",
						"msg": "注册失败",
					})
				} else {
					if reply.Msg == "创建成功" {
						reply.Msg = "注册成功"
					}
					ctx.JSON(int(reply.Code), reply)
					ctx.Next()
				}
			})
		})
	}
}

// @Summary 用户登录
// @Description 用户登录接口
// @Tags Auth
// @Accept x-www-form-urlencoded
// @Produce json
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Router /login [post]
func Login(srv *service.GatewayService) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		us := ctx.PostForm("username")
		pwd := ctx.PostForm("password")

		if us == "" || pwd == "" {
			rp.Failed(ctx, "登录失败", "参数不正确")
			ctx.Abort()
			return
		}

		searchAuth := func(_ context.Context, usc user.UserServiceClient) {
			reply, err := usc.GetUser(ctx, &user.GetUserRequest{
				UserId: strings.Split(auth.GenUUID(us), "-")[0],
			})
			if err != nil || reply.Code != 200 || reply.Data == nil || reply.Data.Role == "" {
				rp.Failed(ctx, "登录失败", "账号密码错误")
				ctx.Abort()
				return
			}

			if reply.Data.Password != auth.HashString(pwd) {
				rp.Failed(ctx, "登录失败", "账号密码错误")
				ctx.Abort()
				return
			} else {
				token, err := srv.Remote.Repo.TokenOperat().GenTokenToRedis(reply.Data.UserId)
				if err != nil {
					middleware.Log.Errorf("Token 生成失败(%s)", err)
				}
				rp.Succeed(ctx, "登陆成功",
					gin.H{
						"user_id": reply.Data.UserId,
						"token":   token,
					},
				)
				ctx.Next()
				return
			}
		}

		srv.Remote.CallGrpc(func(call *biz.APIUseCase) {
			call.Repo.User(searchAuth)
		})
	}
}

func ProxyAuth(srv *service.GatewayService, cRole ...model.AuthType) gin.HandlerFunc {
	return func(ctx *gin.Context) {
		token := ctx.GetHeader("Authorization")
		if token == "" {
			rp.Failed(ctx, "鉴权失败", "未携带Token",
				gin.H{
					"code": 403,
				},
			)
			ctx.Abort()
			return
		}

		token = strings.Replace(token, "Bearer ", "", 1)
		userId, ok := srv.Remote.Repo.TokenOperat().CheckTokenInfo(token)
		if !ok {
			rp.Failed(ctx, "鉴权失败", "Token已过期",
				gin.H{
					"code": 403,
				},
			)
			ctx.Abort()
			return
		}

		searchAuth := func(_ context.Context, usc user.UserServiceClient) {
			reply, err := usc.GetUser(ctx, &user.GetUserRequest{UserId: userId})
			if err != nil || reply.Code != 200 || reply.Data == nil || reply.Data.Role == "" {
				rp.Failed(ctx, "鉴权失败", "用户不存在",
					gin.H{
						"code": 403,
					},
				)
				ctx.Abort()
				return
			}

			isRole := false
			role := reply.Data.Role

			if len(cRole) != 0 {
				for _, r := range cRole {
					if r.String() == role {
						isRole = true
						break
					}
				}
			} else {
				isRole = (model.Admin.String() == role)
			}

			if !isRole {
				rp.Failed(ctx, "无权操作", "当前角色: "+role,
					gin.H{
						"code": 400,
					},
				)
				ctx.Abort()
				return
			}

			ctx.Set("role", role)
			ctx.Set("path", ctx.FullPath())
		}

		srv.Remote.CallGrpc(func(call *biz.APIUseCase) {
			call.Repo.User(searchAuth)
		})

		ctx.Next()
	}
}
